package com.nageffer.shortlink.admin.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nageffer.shortlink.admin.common.biz.user.UserContext;
import com.nageffer.shortlink.admin.common.convention.exception.ClientException;
import com.nageffer.shortlink.admin.dao.entity.GroupDO;
import com.nageffer.shortlink.admin.dao.mapper.GroupMapper;
import com.nageffer.shortlink.admin.dto.req.GroupSortReqDto;
import com.nageffer.shortlink.admin.dto.req.GroupUpdateReqDTO;
import com.nageffer.shortlink.admin.dto.resp.GroupRespDTO;
import com.nageffer.shortlink.admin.remote.ShortLinkActualRemoteService;
import com.nageffer.shortlink.admin.remote.dao.resp.ShortLinkGroupCountRespDTO;
import com.nageffer.shortlink.admin.service.GroupService;
import com.nageffer.shortlink.admin.utils.RandomGenerator;
import groovy.util.logging.Slf4j;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.nageffer.shortlink.admin.common.constant.RedisCacheConstant.LOCK_GROUP_CREATE_KEY;

/**
 * 短链接分组实现层
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO> implements GroupService {

    //使用openfeign做远程调用
    private final ShortLinkActualRemoteService shortLinkActualRemoteService;

    // ShortLinkRemoteService shortLinkRemoteService = new ShortLinkRemoteService() {};

    private final RedissonClient redissonClient;

    @Value("${short-link.group.max-num}")
    private Integer maxGroupNum;

    /**
     * 新增短链接分组
     */
    @Override
    public void saveGroup(String groupName) {
        //在ThreadLocal拿到用户名
        saveGroup(UserContext.getUsername(), groupName);
    }

    @Override
    public void saveGroup(String username, String groupName) {
        RLock lock = redissonClient.getLock(String.format(LOCK_GROUP_CREATE_KEY, username));
        lock.lock();

        try {
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUsername, username)
                    .eq(GroupDO::getDelFlag, 0);
            List<GroupDO> groupDOS = baseMapper.selectList(queryWrapper);
            if (CollUtil.isNotEmpty(groupDOS) && groupDOS.size() == maxGroupNum){
                throw new ClientException("已超出最大分组数：" + maxGroupNum);
            }

            String gid;
            //判断该用户下的gid是否已经存在了，存在的话重新创建一个
            while (true){
                gid = RandomGenerator.generateRandom();
                if (hasGid(username, gid)){
                    break;
                }
            }

            //创建groupDO对象，并插入
            GroupDO groupDO = GroupDO.builder()
                    .gid(gid)
                    .name(groupName)
                    .sortOrder(0)
                    .username(username)
                    .build();
            baseMapper.insert(groupDO);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 判断该用户下的gid是否已经存在了
     * 不存在返回true
     */
    private boolean hasGid(String username, String gid){
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getGid, gid)
                // TODO 在网关拿到登陆用户的用户名
                .eq(GroupDO::getUsername, Optional.ofNullable(username).orElse(UserContext.getUsername()));
        GroupDO one = baseMapper.selectOne(queryWrapper);
        return one == null;
    }

    /**
     * 查询所有短链接分组
     */
    @Override
    public List<GroupRespDTO> listGroup() {
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getDelFlag, 0)
                // 数据库中的null不等于任何值，包括自身，eq传null底层会把sql写成where username = null，所以获取不到值。
                // 所以只能用 where username is null，代码里面的话写成wrapper.isNull(username)就可以获取到了。
                // .isNull(GroupDO::getUsername)
                .eq(GroupDO::getUsername, UserContext.getUsername()) //在ThreadLocal拿到用户名
                .orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);
        List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);
        List<GroupRespDTO> groupRespDTOS = BeanUtil.copyToList(groupDOList, GroupRespDTO.class);

        /**
         * 优化：远程调用拿到每个分组的数量，并把数量赋值给每个GroupRespDTO对象的shortLinkCount属性。
         */

        //从groupDOList中拿到所有的gid集合
        List<String> gidList = groupDOList.stream().map(GroupDO::getGid).toList();
        //根据gidList远程调用拿到所有的ShortLinkGroupCountRespDTO数据
        List<ShortLinkGroupCountRespDTO> remoteResults = shortLinkActualRemoteService.listGroupShortLinkCount(gidList).getData();
        //把拿到的数据转换成map
        Map<String, Integer> countsMap = remoteResults.stream()
                .collect(Collectors.toMap(ShortLinkGroupCountRespDTO::getGid, ShortLinkGroupCountRespDTO::getShortLinkCount));
        //为每个GroupRespDTO设置ShortLinkCount属性
        List<GroupRespDTO> GroupRespDTOList = groupRespDTOS.stream()
                .peek(each -> each.setShortLinkCount(countsMap.getOrDefault(each.getGid(), 0)))
                .toList();
        return GroupRespDTOList;
    }

    /**
     * 更新分组
     */
    @Override
    public void updateGroup(GroupUpdateReqDTO groupUpdateReqDTO) {
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getDelFlag, 0)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getGid, groupUpdateReqDTO.getGid());
        GroupDO groupDO = new GroupDO();
        groupDO.setName(groupUpdateReqDTO.getName());
        baseMapper.update(groupDO, updateWrapper); //mp自带的update会将为null的列过滤掉不更新
    }

    /**
     * 删除分组
     */
    @Override
    public void deleteGroup(String gid) {
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getDelFlag, 0)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getGid, gid);
        GroupDO groupDO = new GroupDO();
        groupDO.setDelFlag(1); //逻辑删除，并没有删除记录
        baseMapper.update(groupDO, updateWrapper);
    }

    /**
     * 排序分组
     */
    @Override
    public void sortGroup(List<GroupSortReqDto> groupSortReqDtoList) {
        //更新集合中的每个元素的sortOrder字段即可
        groupSortReqDtoList.forEach((each) -> {
            GroupDO groupDO = GroupDO.builder()
                    .sortOrder(each.getSortOrder())
                    .build();

            LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                    .eq(GroupDO::getDelFlag, 0)
                    .eq(GroupDO::getUsername, UserContext.getUsername())
                    .eq(GroupDO::getGid, each.getGid());
            baseMapper.update(groupDO, updateWrapper);
        });
    }

}
